package com.not_found404.bilibilimerge;


import android.os.Handler;

import com.google.gson.Gson;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by chengww on 2017/5/28.
 */

public class Tools {

    private Handler handler;

    /**
     * 获取视频名字
     * @param json
     * @return
     */
    public String[] json_getName(String json){
        Gson gson = new Gson();
        Bean bean = gson.fromJson(json,Bean.class);
        String[] names = null;
        if(bean.getPage_data() != null){
            Bean.PageDataBean pageData = bean.getPage_data();
            int page = pageData.getPage();
            String index = String.valueOf(page);
            if(page < 10){
                index = "0" + index;
            }
            //去除不符合命名规则的字符
            String name = nameMatch(pageData.getPart());
            names = new String[]{nameMatch(bean.getTitle()),index + "." + name};
        }else{
            Bean.EpBean ep = bean.getEp();
            String index = ep.getIndex();
            if(index.length() < 2){
                index = "0" + index;
            }
            String name = nameMatch(ep.getIndex_title());
            names = new String[]{nameMatch(bean.getTitle()),index + "." + name};
        }
        return names;
    }

    public boolean deleteFolder(File file){
        if(!file.exists()){
            return false;
        }
        if(file.isFile() || file.listFiles().length == 0){
            file.delete();
            return true;
        }else{
            File[] files = file.listFiles();
            for(int i = 0;i < files.length; i++){
                deleteFolder(files[i]);
            }
            file.delete();
            return true;
        }
    }


    public void doMerge(File in, File out, Handler handler){
        this.handler = handler;
        //1、2、3、4...
        File[] files = in.listFiles();
        //输出路径
        String episode_path = null;
        //循环
        for(File f : files){
            //视频文件名和分P文件名-如第一话
            String[] names = null;
            //获得所有名为.blv的文件
            File[] ffs = null;
            File[] fs = f.listFiles();
            for(final File ff : fs){
                if(ff.getName().equals("entry.json")){
                    String json_name = null;
                    try {
                        BufferedReader reader =
                                new BufferedReader(new InputStreamReader(
                                        new FileInputStream(ff)));
                        json_name = reader.readLine();
                        reader.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    names = json_getName(json_name);

                }

                if(ff.isDirectory() && ff.getName().startsWith("lua.")){
                    //重命名
                    for(int i = 0; i < ff.list().length;i++){
                        File pathname = ff.listFiles()[i];
                        //0.blv -- 00.blv
                        if(pathname.getName().endsWith(".blv") && pathname.getName().length() == 5){
                            pathname.renameTo(new File(pathname.getParentFile().getAbsolutePath() + File.separator + "0" + pathname.getName()));
                        }

                        if(pathname.getName().endsWith(".flv") && pathname.getName().length() == 5){
                            pathname.renameTo(new File(pathname.getParentFile().getAbsolutePath() + File.separator + "0" + pathname.getName()));
                        }

                        //0.blv.bdl -- 00.blv.bdl
                        if(pathname.getName().endsWith(".blv.bdl") && pathname.getName().length() == 9){
                            pathname.renameTo(new File(pathname.getParentFile().getAbsolutePath() + File.separator + "0" + pathname.getName()));
                        }
                    }

                    //过滤得到视频文件
                    ffs = ff.listFiles(new FileFilter() {

                        public boolean accept(File pathname) {
                            for(int i = 0;i < ff.list().length;i++){
                                if(pathname.getName().endsWith(".blv") || pathname.getName().endsWith(".flv") || pathname.getName().endsWith(".blv.bdl")){
                                    return true;
                                }
                            }
                            return false;
                        }
                    });

                    //输出路径
                    episode_path = out.getAbsolutePath() + File.separator + names[0];
                    File episode = new File(episode_path);
                    if(!episode.exists()){
                        episode.mkdirs();
                    }

                    //checkbox_checked
                    handler.obtainMessage(CustomDialogFragment.CONTINUE,
                            "开始合并:" + episode_path).sendToTarget();
                    FlvMerge mFlvMerge = new FlvMerge();
                    try {
                        mFlvMerge.merge(ffs, new File(episode_path + File.separator + names[1] + ".flv"));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public String nameMatch(String str){
        String[] fbsArr = { "\\", "<", ">", "/", "\"",  "[", "]", "?", "^", "*", "|" };
        for (String key : fbsArr) {
            if (str.contains(key)) {
                str = str.replace(key, "-");
            }
        }
        return str;
    }


    public class FlvMerge {
        private final static int FLV_HEADER_SIZE = 9;
        private final static int FLV_TAG_HEADER_SIZE = 11;
        private final static int MAX_DATA_SIZE = 16777220;
        private MyTimeStamp lastTimeStamp;

        public void merge(File[] files,File path) throws IOException {
            String inputFileName, mergeFileName;

            List<FLVContext> mergeTaskList = new ArrayList<FLVContext>();
            Date date = new Date();
            long ti = date.getTime();

            //合并列表只有一个文件
            if(files == null || files.length == 0){
                handler.obtainMessage(CustomDialogFragment.COMPLETE,
                        "文件列表 为空，跳过合并").sendToTarget();
                return;
            }else if (files.length == 1) {
                FileOutputStream writer = new FileOutputStream(path);
                byte[] buffer = new byte[1024];
                int readcount;
                FileInputStream reader = new FileInputStream(files[0]);
                while ((readcount = reader.read(buffer)) != -1) {
                    writer.write(buffer);
                }
                writer.flush();
                writer.close();
                reader.close();
                handler.obtainMessage(CustomDialogFragment.COMPLETE,path.getName() +
                        "复制成功（单文件）").sendToTarget();
                return;
            } else {
                for (int i = 0; i < files.length; i++) {
                    inputFileName = files[i].getAbsolutePath();
                    FLVContext flvCtx = new FLVContext();
                    if (InitFLVContext(flvCtx, inputFileName) != 0) {
                        handler.obtainMessage(CustomDialogFragment.ERROR,"视频合并初始化失败!"+
                                inputFileName).sendToTarget();
                        return;
                    } else {
                        //成功初始化
                        handler.obtainMessage(CustomDialogFragment.CONTINUE,
                                "视频合并：成功初始化\n").sendToTarget();
                        mergeTaskList.add(flvCtx);
                    }

                }
            }

            mergeFileName = path.getAbsolutePath();

            handler.obtainMessage(CustomDialogFragment.CONTINUE,
                    "开始合并"+path.getName()).sendToTarget();
            if (do_merge_tasks(mergeTaskList, mergeFileName) == 0) {
                handler.obtainMessage(CustomDialogFragment.CONTINUE,"-----" +
                        path.getName() + "合并成功!------").sendToTarget();
                ti = new Date().getTime() - ti;
                handler.obtainMessage(CustomDialogFragment.COMPLETE,"耗时 "+
                        ti / 1000000.0 + "s").sendToTarget();
            } else {
                handler.obtainMessage(CustomDialogFragment.ERROR,"合并失败!!!\n").sendToTarget();
            }

        }

        private int do_merge_tasks(List<FLVContext> mergeTaskList,
                                   String mergeFileName) throws IOException {
            File mergeFile = new File(mergeFileName);

            lastTimeStamp = new MyTimeStamp();

            // insure the flv files are suitable to merge
            boolean firstOne = true;
            FLVContext curCtx = null;
            int n = 1;
            for (FLVContext nextFLV : mergeTaskList) {
                if (firstOne) {
                    curCtx = nextFLV;
                    firstOne = false;
                    continue;
                }
                if (!IsSuitableToMerge(curCtx, nextFLV)) {
                    handler.obtainMessage(CustomDialogFragment.CONTINUE,
                            "第"+n+"个视频信息不同，不可合并").sendToTarget();
                    n++;
                } else {
                    handler.obtainMessage(CustomDialogFragment.CONTINUE,
                            "第"+n+"个视频可以合并").sendToTarget();
                    n++;
                }
                curCtx = nextFLV;

            }

            //获得输出流
            FileOutputStream out = new FileOutputStream(mergeFile);
            DataOutputStream dos = new DataOutputStream(out);

            // combine them one by one
            firstOne = true;

            handler.obtainMessage(CustomDialogFragment.CONTINUE,
                    "开始添加FileData.first..\n").sendToTarget();
            FLVContext firstFLV = mergeTaskList.get(0);

            handler.obtainMessage(CustomDialogFragment.CONTINUE,
                    firstFLV.count).sendToTarget();

            if (AddFileData(firstFLV.fileSource, dos, true, lastTimeStamp) != 0) {
                System.exit(-1);
            }
            for (FLVContext nextFLV : mergeTaskList) {
                if (firstOne) {
                    firstOne = false;
                    continue;
                }

                handler.obtainMessage(CustomDialogFragment.CONTINUE,"开始FileData...").sendToTarget();
                handler.obtainMessage(CustomDialogFragment.CONTINUE,nextFLV.count).sendToTarget();
                if (AddFileData(nextFLV.fileSource, dos, false, lastTimeStamp) != 0) {
                    break;
                }


            }

            dos.close();
            return 0;
        }

        private int AddFileData(File fileSource, DataOutputStream dos,
                                boolean isFirstFile, MyTimeStamp lastTimestamp) throws IOException {
            int readLen;
            int dataSize;
            MyTimeStamp curTimestamp = new MyTimeStamp();
            MyTimeStamp newTimestamp = new MyTimeStamp();
            byte[] tmp = new byte[20];
            byte[] buf = new byte[MAX_DATA_SIZE];

            FileInputStream in = new FileInputStream(fileSource);
            DataInputStream dis = new DataInputStream(in);

            // 如果是第一个文件，那么就把第一个文件的头部写到目标文件里面
            if (isFirstFile) {
                handler.obtainMessage(CustomDialogFragment.CONTINUE,"开始dis.read.........").sendToTarget();
                if (FLV_HEADER_SIZE + 4 == dis.read(tmp, 0, FLV_HEADER_SIZE + 4)) {
                    dos.write(tmp, 0, FLV_HEADER_SIZE + 4);
                    dos.flush();
                }
            } else {
                handler.obtainMessage(CustomDialogFragment.CONTINUE,"非第一个文件，跳过头部").sendToTarget();

                if (dis.skip(FLV_HEADER_SIZE + 4) != FLV_HEADER_SIZE + 4) {
                    handler.obtainMessage(CustomDialogFragment.ERROR,"合并失败failed~~").sendToTarget();
                    System.exit(-1);
                }
            }

            while (ReadFromFile(dis, tmp, FLV_TAG_HEADER_SIZE) > 0) {
                dataSize = FromInt24StringBe(tmp, 1);
                //System.out.printf("\ndataSize::::%d\n", dataSize);

			/*for(int i = 4; i < 8;i++)
				System.out.printf("%x,",tmp[i]);*/

                curTimestamp.setTimeStamp(GetTimestamp(tmp, 4));
                newTimestamp.setTimeStamp(curTimestamp.getTimeStamp()
                        + lastTimestamp.getTimeStamp());
                SetTimestamp(tmp, 4, newTimestamp.getTimeStamp());


                if (WriteToFile(dos, tmp, FLV_TAG_HEADER_SIZE) < 0) {
                    dos.close();
                    dis.close();
                    System.exit(-1);
                }

                readLen = dataSize + 4;

                if (ReadFromFile(dis, buf, readLen) > 0) {
                    // System.out.printf("\nReadFromFile(dis, buf, readLen) > 0\n");
                    if (WriteToFile(dos, buf, readLen) < 0) {
                        dos.close();
                        dis.close();
                        System.exit(-1);
                    }
                } else {
                    dos.close();
                    dis.close();
                    System.exit(-1);
                }

            }

            // update the timestamp and return
            lastTimestamp.setTimeStamp(newTimestamp.getTimeStamp());

            handler.obtainMessage(CustomDialogFragment.CONTINUE,lastTimestamp.getTimeStamp()).sendToTarget();
            dis.close();
            return 0;

        }

        public int ReadFromFile(DataInputStream dis, byte[] buffer, int size)
                throws IOException {
            int readLen, realReadLen;
            byte[] tmp;

            readLen = size;
            realReadLen = 0;
            tmp = buffer;

            int readNum = 0;// 已经读取的字节数

            while ((realReadLen = dis.read(tmp, readNum, readLen)) > 0) {
                readLen -= realReadLen;
                readNum += realReadLen;
            }
            return (readLen == 0) ? size : -1;

        }

        public int WriteToFile(DataOutputStream dos, byte[] buffer, int size)
                throws IOException {
            int writeLen;
            byte[] tmp = null;
            writeLen = size;
            tmp = buffer;

		/*
		 * while ( dos.size() < size) { dos.write(tmp, dos.size(), writeLen); }
		 */

            // System.out.print("running writetofile...\n");

            dos.write(tmp, 0, writeLen);
            dos.flush();
            // return (dos.size() == size)?size:-1;

            return 1;
        }

        private boolean IsSuitableToMerge(FLVContext flvCtx1,
                                          FLVContext flvCtx2) {
            return (flvCtx1.soundFormat == flvCtx2.soundFormat)
                    && (flvCtx1.soundRate == flvCtx2.soundRate)
                    && (flvCtx1.soundSize == flvCtx2.soundSize)
                    && (flvCtx1.soundType == flvCtx2.soundType)
                    && (flvCtx1.videoCodecID == flvCtx2.videoCodecID);
        }

        private int InitFLVContext(FLVContext flvCtx, String inputFileName)
                throws IOException {

            flvCtx.fileSource = new File(inputFileName);

//		if (!IsFLVFile(flvCtx.fileSource)) {
//			System.out.printf("%s: invalid FLV file!", inputFileName);
//			System.exit(1);
//		}
            if (GetFLVFileInfo(flvCtx) != 0) {
                handler.obtainMessage(CustomDialogFragment.ERROR,
                        "cannot find flv file info!\n无法找到flv文件信息").sendToTarget();
                System.exit(1);
            }

            return 0;
        }

        private int GetFLVFileInfo(FLVContext flvCtx) throws IOException {
            boolean hasAudioParams, hasVideoParams;
            int skipSize;
            int dataSize;
            int tagType;
            byte[] tmp = new byte[FLV_TAG_HEADER_SIZE + 1];
            if (flvCtx == null)
                return -1;
            skipSize = 9;
            hasVideoParams = hasAudioParams = false;

            FileInputStream in = new FileInputStream(flvCtx.fileSource);
            DataInputStream dis = new DataInputStream(in);
            dis.skip(skipSize);// 跳9个字节

            handler.obtainMessage(CustomDialogFragment.CONTINUE,
                    "此文件的File类型大小："+flvCtx.fileSource.length()).sendToTarget();
            skipSize = 4;
            while (!hasVideoParams || !hasAudioParams) {
                dis.skip(skipSize);// 跳四个字节
                if (dis.read(tmp, 0, FLV_TAG_HEADER_SIZE + 1) != FLV_TAG_HEADER_SIZE + 1) {
                    dis.close();
                    return -1;
                }

                tagType = tmp[0] & 0x1f;
                handler.obtainMessage(CustomDialogFragment.CONTINUE,"tagType:"+tagType).sendToTarget();

                switch (tagType) {
                    case 8:
                        flvCtx.soundFormat = (tmp[FLV_TAG_HEADER_SIZE] & 0xf0) >> 4;
                        flvCtx.soundRate = (tmp[FLV_TAG_HEADER_SIZE] & 0x0c) >> 2;
                        flvCtx.soundSize = (tmp[FLV_TAG_HEADER_SIZE] & 0x02) >> 1;
                        flvCtx.soundType = (tmp[FLV_TAG_HEADER_SIZE] & 0x01) >> 0;
                        hasAudioParams = true;

                        handler.obtainMessage(CustomDialogFragment.CONTINUE,flvCtx.toString()).sendToTarget();
                        break;
                    case 9:
                        flvCtx.videoCodecID = (tmp[FLV_TAG_HEADER_SIZE] & 0x0f);
                        hasVideoParams = true;
                        break;
                    default:
                        break;
                }

                // System.out.printf("%x,%x,%x,%x\n",tmp[0] & 0xff,tmp[1] &
                // 0xff,tmp[2] & 0xff,tmp[3] & 0xff);

                dataSize = FromInt24StringBe(tmp, 1);

                skipSize = dataSize - 1 + 4;

                handler.obtainMessage(CustomDialogFragment.CONTINUE,"数据大小:" + dataSize).sendToTarget();
            }

            dis.close();

            return 0;

        }


        private int FromInt24StringBe(byte[] str, int i) {
            return (str[i] & 0xff) << 16 | (str[i + 1] & 0xff) << 8 | str[i + 2]
                    & 0xff;
        }

        private long GetTimestamp(byte[] tmp, int i) {

            return (
                    (tmp[i + 3] << 24 & 0xFF000000) |
                            (tmp[i] << 16 & 0xFF0000) |
                            (tmp[i + 1] << 8 & 0xFF00) |
                            (tmp[i + 2] & 0xFF));
        }

        private void SetTimestamp(byte[] tmp, int i, long newTimestamp) {
            tmp[i + 3] = (byte)(newTimestamp >> 24);
            tmp[i] =  (byte)(newTimestamp >> 16);
            tmp[i + 1] = (byte)(newTimestamp >> 8);
            tmp[i + 2] = (byte)(newTimestamp);
        }

    }

    class MyTimeStamp {
        public long timeStamp;

        public MyTimeStamp() {
            timeStamp = 0;
        }

        public MyTimeStamp(long time) {
            timeStamp = time;
        }

        public long getTimeStamp() {
            return timeStamp;
        }

        public void setTimeStamp(long timeStamp) {
            this.timeStamp = timeStamp;
        }

    }

    class FLVContext {

        int soundFormat;
        int soundRate;
        int soundSize;
        int soundType;
        int videoCodecID;

        File fileSource;
        FLVContext next;
        int count;

        public String toString() {
            return "\nsoundFormat:" + this.soundFormat + "\nsoundRate:"
                    + this.soundRate + "\nsoundSize:" + this.soundType
                    + "\nsoundType:" + this.soundType + "\nvideoCodeId:"
                    + this.videoCodecID + "\n";
        }

    }
}

